Java 反射

什么是反射?

反射(Reflection),它允许运行中的Java程序获取自身的信息,并且可以操作类或对象的内部属性。

在Java中我们认为一切皆对象,那么对于类本身其实也是一个对象,那么它是谁的对象呢?其实它是Class类的对象。对于类来说它是Class类的类类型。

通过反射,我们可以在运行时获得程序或者程序集中每一个类型的成员和成员信息。程序中一般对象的类型在编译的时候就确定下来,通过反射机制,可以动态地创建对象并调用其属性。

通过反射,可以绕过编译期,在运行时进行创建和使用 类/方法/属性。

Java反射主要功能:

  • 在运行时判断任意一个对象说属的类;
  • 在运行时任意构造一个类的对象;
  • 在运行时任意判断一个类的成员变量/方法;
  • 在运行时任意调用一个对象的方法

获取Class对象的三种方法

1.使用Class类的forName方法

1
2
3
4
5
6
7
// 需要异常处理
try {
// myClass 需要用全部路径
Class c = Class.forName("cn.eilene.demo.Myclass");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}

2.直接获取某一个对象的class

1
2
3
4
5
Class c = String.class;
System.out.println(c.getName());
// out: java.lang.String
System.out.println(c.getSimpleName());
// out: String

3.调用对象的getClass()方法

1
2
myClass mc = new myClass();
Class c = mc.getClass();

判断某个对象是否是某个类的实例

1
2
boolean isIn = String.class.isInstance(str);
// 判断 str 是否为 String 类的实例

创建实例

1
2
3
4
5
6
7
8
9
try {
Object s = String.class.newInstance();
boolean is = s instanceof String;
// out: isIn is true;
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}

获取类的信息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
Class c = myClass.class;

int modifiers = c.getModifiers();
// 数字表示该类的修饰符 public/private/static
Package pack = c.getPackage();

// 获取包名
Class supc = c.getSuperclass();

// 获取父类
Class[] interfaces = c.getInterfaces();

// 获取类实现的接口集合
Constructor[] con = c.getDeclaredConstructors();

// 获取类的构造集合
for(Constructor constructor: con){
Class[] paramTypes = constructor.getParameterTypes();
}

// 获取类指定的构造方法, 需要指定参数列表的类类型。并加上异常处理
try {
Constructor cs = c.getConstructor(String.class, int.class);
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (SecurityException e) {
e.printStackTrace();
}

// 获取所有public的方法
Method[] methods = c.getMethods();

// 获取指定的方法 name is String, paramTypes 参数列表
Method method = c.getMethod(name, paramTypes)


// 获取类的成员变量(字段)信息
// 得到public的成员变量
Field[] fields1 = c.getFields();
// 得到所有已经声明的成员变量(private/public/protected),但不能得到父类的成员变量,
Field[] fields2 = c.getDeclaredFields();

调用方法

1
2
3
4
5
6
7
8
9
10
11
Class c = myClass.class;
try {
Object obj = c.newInstance();
Method method = c.getMethod("setName", String.class);
Object result = method.invoke(obj, "Mingyang");
Method method1 = c.getMethod("getName");
Object result1 = method1.invoke(obj);
System.out.println(result1);
} catch (Exception e) {
e.printStackTrace();
}

获取类类型(类对象)注意事项

  1. 获取类对象的时候会导致类静态属性被初始化,而且只初始化一次,但 Class = myClass.class 不会导致静态属性被初始化。
  2. 前面说过,使用反射可以绕过编译,而在运行时动态的创建一个对象。因为通常我们创建实例的时候是通过new关键字,这时候如果类型不符,在编译阶段就会报错。而反射是在运行阶段的时候才被调用,因此会带来安全的问题。有利也有弊。

参考文章

  1. 深入解析Java反射(1) - 基础
  2. Java Reflection(反射机制)详解
  3. JAVA反射机制教程
  4. 反射——Java高级开发必须懂的